@fieldwangai/agentflow 0.1.71 → 0.1.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lib/admin-storage-config.mjs +90 -5
- package/bin/lib/composer-skill-router.mjs +9 -1
- package/bin/lib/paths.mjs +28 -0
- package/bin/lib/skill-registry.mjs +20 -7
- package/bin/lib/ui-server.mjs +539 -63
- package/builtin/web-ui/dist/assets/index-DKw0bt1j.js +297 -0
- package/builtin/web-ui/dist/assets/index-t1Xrjvhr.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/builtin/web-ui/dist/assets/index-B3Wr-I3p.css +0 -1
- package/builtin/web-ui/dist/assets/index-C8JMDUZb.js +0 -296
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
expandAgentflowHomePath,
|
|
8
8
|
getAgentflowDataRoot,
|
|
9
9
|
getAgentflowDataRootOverride,
|
|
10
|
+
getAgentflowSkillsRoot,
|
|
11
|
+
getAgentflowSkillsRootOverride,
|
|
10
12
|
writeAgentflowDataRootOverride,
|
|
13
|
+
writeAgentflowSkillsRootOverride,
|
|
11
14
|
} from "./paths.mjs";
|
|
12
15
|
|
|
13
16
|
function defaultAgentflowDataRoot() {
|
|
@@ -28,6 +31,14 @@ function normalizeDataRoot(raw) {
|
|
|
28
31
|
return dataRoot;
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
function normalizeSkillsRoot(raw) {
|
|
35
|
+
const skillsRoot = expandAgentflowHomePath(raw);
|
|
36
|
+
if (skillsRoot && !path.isAbsolute(skillsRoot)) {
|
|
37
|
+
throw new Error("skillsRoot must be an absolute path");
|
|
38
|
+
}
|
|
39
|
+
return skillsRoot;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
function copyEntry(src, dest) {
|
|
32
43
|
const stat = fs.lstatSync(src);
|
|
33
44
|
if (stat.isSymbolicLink()) {
|
|
@@ -45,6 +56,12 @@ function copyEntry(src, dest) {
|
|
|
45
56
|
fs.copyFileSync(src, dest);
|
|
46
57
|
}
|
|
47
58
|
|
|
59
|
+
function copyEntryIfMissing(src, dest) {
|
|
60
|
+
if (fs.existsSync(dest)) return false;
|
|
61
|
+
copyEntry(src, dest);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
48
65
|
function migrateDataRoot(oldRoot, newRoot) {
|
|
49
66
|
const from = path.resolve(oldRoot);
|
|
50
67
|
const to = path.resolve(newRoot);
|
|
@@ -63,9 +80,49 @@ function migrateDataRoot(oldRoot, newRoot) {
|
|
|
63
80
|
return { migrated: true, copied: true };
|
|
64
81
|
}
|
|
65
82
|
|
|
83
|
+
function legacyCodexSkillsRoot() {
|
|
84
|
+
return path.join(os.homedir(), ".codex", "skills");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function migrateSkillsRoot(oldRoot, newRoot) {
|
|
88
|
+
const from = path.resolve(oldRoot);
|
|
89
|
+
const to = path.resolve(newRoot);
|
|
90
|
+
if (from === to) return { migrated: false, copied: false };
|
|
91
|
+
if (pathInside(from, to)) {
|
|
92
|
+
throw new Error("skillsRoot target cannot be inside the current skills root");
|
|
93
|
+
}
|
|
94
|
+
if (pathInside(to, from)) {
|
|
95
|
+
throw new Error("skillsRoot target cannot be a parent of the current skills root");
|
|
96
|
+
}
|
|
97
|
+
fs.mkdirSync(to, { recursive: true });
|
|
98
|
+
if (!fs.existsSync(from)) return { migrated: true, copied: false };
|
|
99
|
+
for (const name of fs.readdirSync(from)) {
|
|
100
|
+
copyEntry(path.join(from, name), path.join(to, name));
|
|
101
|
+
}
|
|
102
|
+
return { migrated: true, copied: true };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function migrateLegacyCodexSkills(targetRoot) {
|
|
106
|
+
const from = legacyCodexSkillsRoot();
|
|
107
|
+
const to = path.resolve(targetRoot);
|
|
108
|
+
const source = path.resolve(from);
|
|
109
|
+
if (source === to || !fs.existsSync(source)) return { migrated: false, copied: 0, source };
|
|
110
|
+
if (pathInside(source, to) || pathInside(to, source)) return { migrated: false, copied: 0, source };
|
|
111
|
+
fs.mkdirSync(to, { recursive: true });
|
|
112
|
+
let copied = 0;
|
|
113
|
+
for (const name of fs.readdirSync(source)) {
|
|
114
|
+
if (copyEntryIfMissing(path.join(source, name), path.join(to, name))) copied += 1;
|
|
115
|
+
}
|
|
116
|
+
return { migrated: copied > 0, copied, source };
|
|
117
|
+
}
|
|
118
|
+
|
|
66
119
|
export function readAdminStorageConfig() {
|
|
67
120
|
const envDataRoot = normalizeDataRoot(process.env.AGENTFLOW_HOME || "");
|
|
121
|
+
const envSkillsRoot = normalizeSkillsRoot(process.env.AGENTFLOW_SKILLS_ROOT || "");
|
|
68
122
|
const configuredDataRoot = getAgentflowDataRootOverride();
|
|
123
|
+
const configuredSkillsRoot = getAgentflowSkillsRootOverride();
|
|
124
|
+
const skillsRoot = getAgentflowSkillsRoot();
|
|
125
|
+
const legacySkillsRoot = legacyCodexSkillsRoot();
|
|
69
126
|
return {
|
|
70
127
|
version: 1,
|
|
71
128
|
dataRoot: getAgentflowDataRoot(),
|
|
@@ -73,21 +130,49 @@ export function readAdminStorageConfig() {
|
|
|
73
130
|
defaultDataRoot: defaultAgentflowDataRoot(),
|
|
74
131
|
envDataRoot,
|
|
75
132
|
envLocked: Boolean(envDataRoot),
|
|
133
|
+
skillsRoot,
|
|
134
|
+
configuredSkillsRoot,
|
|
135
|
+
defaultSkillsRoot: path.join(getAgentflowDataRoot(), "skills"),
|
|
136
|
+
envSkillsRoot,
|
|
137
|
+
skillsEnvLocked: Boolean(envSkillsRoot),
|
|
138
|
+
legacySkillsRoot,
|
|
139
|
+
legacySkillsRootExists: fs.existsSync(legacySkillsRoot),
|
|
76
140
|
configPath: AGENTFLOW_HOME_CONFIG_PATH,
|
|
77
141
|
};
|
|
78
142
|
}
|
|
79
143
|
|
|
80
144
|
export function writeAdminStorageConfig(config = {}) {
|
|
81
145
|
const current = readAdminStorageConfig();
|
|
82
|
-
if (current.envLocked) {
|
|
83
|
-
throw new Error("AGENTFLOW_HOME is set; update the environment variable instead of Admin Settings");
|
|
84
|
-
}
|
|
85
146
|
const nextRoot = normalizeDataRoot(config.dataRoot ?? "");
|
|
86
147
|
if (!nextRoot) throw new Error("dataRoot is required");
|
|
87
|
-
|
|
88
|
-
|
|
148
|
+
let migration = { migrated: false, copied: false };
|
|
149
|
+
if (current.envLocked) {
|
|
150
|
+
if (path.resolve(nextRoot) !== path.resolve(current.dataRoot)) {
|
|
151
|
+
throw new Error("AGENTFLOW_HOME is set; update the environment variable instead of Admin Settings");
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
migration = migrateDataRoot(current.dataRoot, nextRoot);
|
|
155
|
+
writeAgentflowDataRootOverride(nextRoot);
|
|
156
|
+
}
|
|
157
|
+
const afterDataRoot = readAdminStorageConfig();
|
|
158
|
+
if (afterDataRoot.skillsEnvLocked) {
|
|
159
|
+
return {
|
|
160
|
+
...afterDataRoot,
|
|
161
|
+
migration,
|
|
162
|
+
skillsMigration: { skipped: true, reason: "AGENTFLOW_SKILLS_ROOT is set" },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const nextSkillsRoot = normalizeSkillsRoot(config.skillsRoot ?? afterDataRoot.defaultSkillsRoot);
|
|
166
|
+
if (!nextSkillsRoot) throw new Error("skillsRoot is required");
|
|
167
|
+
const skillsMigration = migrateSkillsRoot(current.skillsRoot, nextSkillsRoot);
|
|
168
|
+
writeAgentflowSkillsRootOverride(nextSkillsRoot);
|
|
169
|
+
const legacySkillsMigration = config.migrateLegacySkills === false
|
|
170
|
+
? { migrated: false, copied: 0, source: legacyCodexSkillsRoot(), skipped: true }
|
|
171
|
+
: migrateLegacyCodexSkills(nextSkillsRoot);
|
|
89
172
|
return {
|
|
90
173
|
...readAdminStorageConfig(),
|
|
91
174
|
migration,
|
|
175
|
+
skillsMigration,
|
|
176
|
+
legacySkillsMigration,
|
|
92
177
|
};
|
|
93
178
|
}
|
|
@@ -137,18 +137,26 @@ export function readComposerSkillDetail(packageRoot, workspaceRoot, keyOrName) {
|
|
|
137
137
|
return registryReadSkillDetail(packageRoot, workspaceRoot, keyOrName);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
function skillNameFromKey(keyOrName) {
|
|
141
|
+
const value = String(keyOrName || "").trim();
|
|
142
|
+
if (!value) return "";
|
|
143
|
+
const idx = value.indexOf(":");
|
|
144
|
+
return idx >= 0 ? value.slice(idx + 1).trim() : value;
|
|
145
|
+
}
|
|
146
|
+
|
|
140
147
|
export function loadResourcesForSkillKeys(skillKeys, packageRoot, workspaceRoot) {
|
|
141
148
|
if (!Array.isArray(skillKeys) || skillKeys.length === 0) {
|
|
142
149
|
return { skills: [], references: [], skillsHint: "", hasContext: false };
|
|
143
150
|
}
|
|
144
151
|
const wanted = new Set(skillKeys.map((x) => String(x || "").trim()).filter(Boolean));
|
|
145
152
|
if (wanted.size === 0) return { skills: [], references: [], skillsHint: "", hasContext: false };
|
|
153
|
+
const wantedNames = new Set(Array.from(wanted).map(skillNameFromKey).filter(Boolean));
|
|
146
154
|
|
|
147
155
|
const exactByKey = new Map(registryListSkills(packageRoot, workspaceRoot).map((item) => [item.key, item]));
|
|
148
156
|
const candidateItems = [];
|
|
149
157
|
const seenKeys = new Set();
|
|
150
158
|
for (const item of registryListUniqueSkills(packageRoot, workspaceRoot)) {
|
|
151
|
-
if (!wanted.has(item.key) && !wanted.has(item.name)) continue;
|
|
159
|
+
if (!wanted.has(item.key) && !wanted.has(item.name) && !wantedNames.has(item.name)) continue;
|
|
152
160
|
candidateItems.push(item);
|
|
153
161
|
seenKeys.add(item.key);
|
|
154
162
|
}
|
package/bin/lib/paths.mjs
CHANGED
|
@@ -49,6 +49,11 @@ export function getAgentflowDataRootOverride() {
|
|
|
49
49
|
return expandAgentflowHomePath(config.dataRoot || "");
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
export function getAgentflowSkillsRootOverride() {
|
|
53
|
+
const config = readAgentflowHomeConfig();
|
|
54
|
+
return expandAgentflowHomePath(config.skillsRoot || "");
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
export function writeAgentflowDataRootOverride(dataRoot) {
|
|
53
58
|
const nextRoot = expandAgentflowHomePath(dataRoot);
|
|
54
59
|
if (nextRoot && !path.isAbsolute(nextRoot)) {
|
|
@@ -62,6 +67,19 @@ export function writeAgentflowDataRootOverride(dataRoot) {
|
|
|
62
67
|
return nextRoot;
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
export function writeAgentflowSkillsRootOverride(skillsRoot) {
|
|
71
|
+
const nextRoot = expandAgentflowHomePath(skillsRoot);
|
|
72
|
+
if (nextRoot && !path.isAbsolute(nextRoot)) {
|
|
73
|
+
throw new Error("skillsRoot must be an absolute path");
|
|
74
|
+
}
|
|
75
|
+
const current = readAgentflowHomeConfig();
|
|
76
|
+
const next = { ...current, skillsRoot: nextRoot };
|
|
77
|
+
if (!nextRoot) delete next.skillsRoot;
|
|
78
|
+
fs.mkdirSync(path.dirname(AGENTFLOW_HOME_CONFIG_PATH), { recursive: true });
|
|
79
|
+
fs.writeFileSync(AGENTFLOW_HOME_CONFIG_PATH, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
80
|
+
return nextRoot;
|
|
81
|
+
}
|
|
82
|
+
|
|
65
83
|
export function getAgentflowDataRoot() {
|
|
66
84
|
const env = process.env.AGENTFLOW_HOME;
|
|
67
85
|
if (env != null && String(env).trim() !== "") {
|
|
@@ -72,6 +90,16 @@ export function getAgentflowDataRoot() {
|
|
|
72
90
|
return path.join(os.homedir(), "agentflow");
|
|
73
91
|
}
|
|
74
92
|
|
|
93
|
+
export function getAgentflowSkillsRoot() {
|
|
94
|
+
const env = process.env.AGENTFLOW_SKILLS_ROOT;
|
|
95
|
+
if (env != null && String(env).trim() !== "") {
|
|
96
|
+
return expandAgentflowHomePath(env);
|
|
97
|
+
}
|
|
98
|
+
const configured = getAgentflowSkillsRootOverride();
|
|
99
|
+
if (configured) return configured;
|
|
100
|
+
return path.join(getAgentflowDataRoot(), "skills");
|
|
101
|
+
}
|
|
102
|
+
|
|
75
103
|
export const AGENTFLOW_DEFAULT_USER_ID = "";
|
|
76
104
|
|
|
77
105
|
export function sanitizeAgentflowUserId(userId) {
|
|
@@ -2,6 +2,7 @@ import fs from "fs";
|
|
|
2
2
|
import os from "os";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import yaml from "js-yaml";
|
|
5
|
+
import { getAgentflowSkillsRoot } from "./paths.mjs";
|
|
5
6
|
|
|
6
7
|
const fileCache = new Map();
|
|
7
8
|
const CACHE_TTL_MS = 60_000;
|
|
@@ -9,10 +10,11 @@ const SOURCE_PRIORITY = new Map([
|
|
|
9
10
|
["workspace-agents", 100],
|
|
10
11
|
["workspace-codex", 95],
|
|
11
12
|
["workspace-cursor", 90],
|
|
13
|
+
["agentflow-root", 85],
|
|
12
14
|
["builtin", 80],
|
|
13
|
-
["global-agents",
|
|
14
|
-
["global-codex",
|
|
15
|
-
["global-cursor",
|
|
15
|
+
["global-agents", 55],
|
|
16
|
+
["global-codex", 50],
|
|
17
|
+
["global-cursor", 45],
|
|
16
18
|
]);
|
|
17
19
|
|
|
18
20
|
function readFileCached(absPath) {
|
|
@@ -28,6 +30,10 @@ function readFileCached(absPath) {
|
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
export function clearSkillRegistryCache() {
|
|
34
|
+
fileCache.clear();
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
export function stripSkillFrontmatter(content) {
|
|
32
38
|
const raw = String(content || "");
|
|
33
39
|
const match = raw.match(/^---\s*\r?\n[\s\S]*?\r?\n---\s*\r?\n?/);
|
|
@@ -109,11 +115,17 @@ export function defaultSkillSources(packageRoot, workspaceRoot) {
|
|
|
109
115
|
},
|
|
110
116
|
];
|
|
111
117
|
if (workspaceRoot) sources.push(...workspaceSkillSources(workspaceRoot, "workspace", "工作区"));
|
|
118
|
+
sources.push({
|
|
119
|
+
source: "agentflow-root",
|
|
120
|
+
sourceLabel: "AgentFlow Skills",
|
|
121
|
+
dir: getAgentflowSkillsRoot(),
|
|
122
|
+
installedBy: "agentflow-admin",
|
|
123
|
+
});
|
|
112
124
|
const home = os.homedir();
|
|
113
125
|
sources.push(
|
|
114
|
-
{ source: "global-agents", sourceLabel: "
|
|
115
|
-
{ source: "global-cursor", sourceLabel: "
|
|
116
|
-
{ source: "global-codex", sourceLabel: "
|
|
126
|
+
{ source: "global-agents", sourceLabel: "Legacy ~/.agents", dir: path.join(home, ".agents", "skills"), installedBy: "global" },
|
|
127
|
+
{ source: "global-cursor", sourceLabel: "Legacy ~/.cursor", dir: path.join(home, ".cursor", "skills"), installedBy: "global" },
|
|
128
|
+
{ source: "global-codex", sourceLabel: "Legacy ~/.codex", dir: path.join(home, ".codex", "skills"), installedBy: "skillhub-legacy" },
|
|
117
129
|
);
|
|
118
130
|
return sources;
|
|
119
131
|
}
|
|
@@ -183,9 +195,10 @@ export function listUniqueSkills(packageRoot, workspaceRoot, opts = {}) {
|
|
|
183
195
|
export function readSkillDetail(packageRoot, workspaceRoot, keyOrName) {
|
|
184
196
|
const wanted = String(keyOrName || "").trim();
|
|
185
197
|
if (!wanted) return null;
|
|
198
|
+
const wantedName = wanted.includes(":") ? wanted.slice(wanted.indexOf(":") + 1).trim() : wanted;
|
|
186
199
|
const all = listSkills(packageRoot, workspaceRoot);
|
|
187
200
|
const item = all.find((skill) => skill.key === wanted)
|
|
188
|
-
|| dedupeSkillsByName(all).find((skill) => skill.name === wanted);
|
|
201
|
+
|| dedupeSkillsByName(all).find((skill) => skill.name === wanted || skill.name === wantedName);
|
|
189
202
|
if (!item) return null;
|
|
190
203
|
return item;
|
|
191
204
|
}
|